import { Component, ContentChildren, EventEmitter, Input, OnInit, Output, QueryList, TemplateRef } from '@angular/core';
import { AbstractControl, AsyncValidator, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { debounceTime, map } from 'rxjs/operators';
import { YbyFormRefDirective } from './directives/form-ref.directive';
import { FormItemLayout, FormGroupItem, CheckMode } from './form-group.interfaces';

@Component({
  selector: 'yby-form-group',
  templateUrl: './form-group.component.html',
  styleUrls: ['./form-group.component.scss']
})
export class FormGroupComponent implements OnInit {
  
  @Input() form!: FormGroup
  @Input() items: FormGroupItem[] = [];
  @Input() delayTime = 100;
  @Input() itemLayout: FormItemLayout = { label: 6, cont: 18 };
  @Input() cols = 1;
  @Input() showAction = true;
  @Input() actionSpan = 24;
  @Input() tpl!: TemplateRef<any>;
  @Output() valueChange: EventEmitter<any> = new EventEmitter();
  @Output() resetForm: EventEmitter<any> = new EventEmitter();

  // 获取嵌套的子元素组合-ChildRefDirective
  @ContentChildren(YbyFormRefDirective) templates!: QueryList<YbyFormRefDirective>;
  get formRefs(){
    const aObj: any = {};
    this.templates.forEach(template => {
      const key: string = template.ybyFormRef;
      aObj[key] = template;
    })
    return aObj;
  }
  formGroup: FormGroup = new FormGroup({});

  itemObj: any = {};
  tipObj: any = {};
  CheckMode = CheckMode;

  constructor(
    private fb: FormBuilder,
  ) { }

  ngOnInit() {
    this.formInit();
    this.form.valueChanges.pipe(
      debounceTime(this.delayTime)
    ).subscribe(res => {
      this.valueChange.emit(res);
    });
  }

  setDefaultValue() {
    const valueObj: any = {};
    this.items.forEach(item => {
      valueObj[item.name] = item.defaultValue;
    });
    this.form.patchValue(valueObj);
  }

  formInit() {
    const options: any = {};
    this.items.forEach(item => {
      this.itemObj[`${item.name}`] = () => this.form.get(item.name);
      const { validators, asyncValidators, errorTipObj } = this.reGroupValidators(item.rules);
      this.tipObj[item.name] = errorTipObj;
      options[item.name] = [
        item.defaultValue,
        {
          updateOn: item.updateOn || 'change',
          validators,
          asyncValidators
        }
      ];
    });
    this.form = this.fb.group({
      ...options,
    });
    this.resetForm.emit(this.form);
  }

  setItemDisabled(key: string, disabled: boolean) {
    const aItem = this.form.get(key);
    if (!aItem) {
      return;
    }
    if (disabled) {
      aItem.disable();
    } else {
      aItem.enable();
    }
  }

  reGroupValidators(rules: any[] | undefined) {
    const validators: Validators[] = [];
    const asyncValidators: ((c: AbstractControl) => AsyncValidator)[] = [];
    const errorTipObj: any = {};
    if (rules) {
      rules.forEach((rule, index) => {
        if (!rule.async) {
          let aVerify: Validators;
          if (rule.required) {
            aVerify = (c: AbstractControl) => {
              return c.value ? null : { [index]: true };
            };
          } else if (rule.minLength) {
            aVerify = (c: AbstractControl) => {
              return c.value && c.value.length >= rule.minLength ? null : { [index]: true };
            };
          } else if (rule.maxLength) {
            aVerify = (c: AbstractControl) => {
              return c.value && c.value.length <= rule.maxLength ? null : { [index]: true };
            };
          } else if (rule.pattern) {
            aVerify = (c: AbstractControl) => {
              const reg = new RegExp(rule.pattern);
              return reg.test(c.value) ? null : { [index]: true };
            };
          } else if (rule.selfVerify) {
            aVerify = (c: AbstractControl) => {
              const flag = rule.selfVerify(c.value);
              return flag ? null : { [index]: true };
            };
          } else {
            aVerify = (c: AbstractControl) => {
              return null;
            };
          }
          validators.push(aVerify);
        } else {
          asyncValidators.push((c: AbstractControl): AsyncValidator => {
            return rule.selfVerify(c.value).pipe(
              map(flag => {
                return flag ? null : { [index]: true };
              })
            );
          });
        }
        errorTipObj[index] = rule.msg;
      });
    }
    return {
      validators,
      asyncValidators,
      errorTipObj,
    };
  }

  verify() {
    const form = this.form;
    for (const i in form.controls) {
      if (form.controls[i]) {
        form.controls[i].markAsDirty();
        form.controls[i].updateValueAndValidity();
      }
    }
  }

  setClass(item: any) {
    const aItem = this.form.get(item.name);
    if (!aItem) {
      return '';
    }
    const errors = aItem.errors;
    return !errors ? '' : 'ant-form-item-with-help';
  }

  setErrorTip(item: any): string {
    const aItem = this.form.get(item.name);
    if (!aItem) {
      return '';
    }
    const errors = aItem.errors;
    if (!errors) {
      return '';
    }
    let msg = '';
    for (const key in errors) {
      if (Object.prototype.hasOwnProperty.call(errors, key)) {
        const ele = errors[key];
        if (ele) {
          msg = !msg ? this.tipObj[item.name][key] : msg;
        }
      }
    }
    return msg;
  }

}
